package com.gdteam.kernel.main;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.URL;
import java.util.Properties;

import org.apache.tapestry5.ioc.MappedConfiguration;
import org.apache.tapestry5.ioc.ScopeConstants;
import org.apache.tapestry5.ioc.ServiceBinder;

import com.gdteam.kernel.conf.Environment;
import com.gdteam.kernel.content.IndexManager;
import com.gdteam.kernel.content.impl.DefaultIndexManager;
import com.gdteam.kernel.domain.DomainModelDAOFactory;
import com.gdteam.kernel.domain.DomainModelFactory;
import com.gdteam.kernel.domain.impl.DomainModelDAOFactoryImpl;
import com.gdteam.kernel.domain.impl.DomainModelFactoryImpl;
import com.gdteam.kernel.event.Context;
import com.gdteam.kernel.event.Event;
import com.gdteam.kernel.event.EventFactory;
import com.gdteam.kernel.event.EventManager;
import com.gdteam.kernel.event.impl.DefaultContext;
import com.gdteam.kernel.event.impl.DefaultEvent;
import com.gdteam.kernel.event.impl.DefaultEventFactory;
import com.gdteam.kernel.event.impl.DefaultEventManager;
import com.gdteam.kernel.httpserver.HttpServer;
import com.gdteam.kernel.httpserver.impl.RestletHttpServer;
import com.gdteam.kernel.i18n.I18nManager;
import com.gdteam.kernel.i18n.impl.DefaultI18nManager;
import com.gdteam.kernel.main.impl.DefaultServiceManager;
import com.gdteam.kernel.persistence.EntityProvider;
import com.gdteam.kernel.persistence.PersistencyUnitManager;
import com.gdteam.kernel.persistence.impl.DefaultPersistencyUnitManager;
import com.gdteam.kernel.persistence.impl.KernelEntityProvider;
import com.gdteam.kernel.timer.TimerManager;
import com.gdteam.kernel.timer.impl.DefaultTimerManager;

/**
 * KernelModule operation class
 * @author erwan
 */
public class KernelModule  {

    static final int LOWER_PORT_RANGE = 8082;
    static final int UPPER_PORT_RANGE = 8990;
    
    /**
     * Binding step
     * @param binder
     */
    public static void bind(ServiceBinder binder) {
        
        //kernel tech services binding
        binder.bind(I18nManager.class, DefaultI18nManager.class).withId(I18nManager.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(ServiceManager.class,DefaultServiceManager.class).withId(ServiceManager.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(TimerManager.class,DefaultTimerManager.class).withId(TimerManager.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(PersistencyUnitManager.class,DefaultPersistencyUnitManager.class).withId(PersistencyUnitManager.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(DomainModelFactory.class,DomainModelFactoryImpl.class).withId(DomainModelFactory.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(DomainModelDAOFactory.class,DomainModelDAOFactoryImpl.class).withId(DomainModelDAOFactory.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(HttpServer.class, RestletHttpServer.class).withId(HttpServer.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(EntityProvider.class,KernelEntityProvider.class).withId(EntityProvider.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(EventFactory.class,DefaultEventFactory.class).withId(EventFactory.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(EventManager.class, DefaultEventManager.class).withId(EventManager.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(Event.class,DefaultEvent.class).withId(Event.FQN).scope(ScopeConstants.PERTHREAD).eagerLoad();
        binder.bind(Context.class,DefaultContext.class).withId(Context.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
        binder.bind(IndexManager.class,DefaultIndexManager.class).withId(IndexManager.FQN).scope(ScopeConstants.DEFAULT).eagerLoad();
    }
    
    /**
     * Core configuration part
     * @param configuration
     */
    public void contributeApplicationDefaults(MappedConfiguration<String, String> configuration) {
        
        // Adding Leyio Home Property settings
        String gdteamhome  = System.getProperty("user.home")+File.separator+".gdteam";
        
        File home = new File(gdteamhome);
        home.mkdirs();
        
        File log = new File(home,"log");
        File data = new File(home,"data");
        File news = new File(home,"news");
        log.mkdirs();
        data.mkdirs();
        news.mkdirs();
        
        log = null;
        data = null;
        news = null;
        
        //System.setProperty(Environment.LEYIO_HOME, home.getPath());
        configuration.add(Environment.GDTEAM_HOME, home.getPath());
        
        
        //Global properties overriding file
        Properties props = new Properties();

        try {
           
            URL url = null;
            
            String configurationpath = System.getProperty(Environment.OVERRIDING_PROPS_FILE_PATH);
            
            if (null != configurationpath) {
                url = new URL(configurationpath);
            }
            
            InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(Environment.PROPS_FILE);
            
            props.load(is);

            
            if (null != configurationpath) {
                try {
                    is = new FileInputStream(url.getFile());
                    props.load(is);
                } catch (IOException ioe) { 
                }
            }
            
            for (Object key: props.keySet()){ 
                configuration.add((String)key, (String)props.getProperty((String)key));
            }
            
            
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        
        // server configuration contribution
        contributeServerConfiguration(configuration);        
    }
    
    
    /**
     * Return available port
     * @return
     */
    private void contributeServerConfiguration(MappedConfiguration<String, String> configuration) {
        
        for(int i = LOWER_PORT_RANGE; i <= UPPER_PORT_RANGE; i++) {

            if (isPortAvailable(i)) {
                configuration.add(Environment.HTTP_SERVER_PORT, ""+i);
                return;
            }
        }
        throw new RuntimeException("Could not initialize port configuration");
    }

    /**
     * Return true if specified port is available
     * @param port
     * @return true if specified port is available
     */
    private boolean isPortAvailable(int port) {
        ServerSocket srv = null;
        try {
            srv = new ServerSocket(port);
            return true;
        } catch (IOException e) {
            return false;
        } finally {
            if (srv != null) {
                try {
                    srv.close();
                } catch (IOException e) {
                    //Do nothing
                }
                srv = null;
            }
        }
    }
    
}
